/*
 * Copyright (C)  Justson(https://github.com/Justson/Downloader)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.hunter.download;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.mktwo.network.HttpLog;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;

/**
 * @author cenxiaozhong
 * @date 2019/2/9
 * @since 1.0.0
 */
public class DownloadRequest<T extends DownloadTask> {

    static final String TAG = Runtime.PREFIX + DownloadRequest.class.getSimpleName();
    private DownloadTask mDownloadTask;
    static final Handler mHandle = new Handler(Looper.getMainLooper());


    static DownloadRequest with(Context context) {
        DownloadRequest downloadRequest = new DownloadRequest();
        downloadRequest.mDownloadTask = Runtime.getInstance().getDefaultDownloadTask();
        downloadRequest.mDownloadTask.setContext(context);
        return downloadRequest;
    }

    public DownloadRequest url(@NonNull String url) {
        mDownloadTask.setUrl(url);
        return this;
    }

    private DownloadRequest target(@Nullable File target) {
        File file = target;
        if (!file.exists()) {
            try {
                File parent = file.getParentFile();
                if (!parent.exists()) {
                    parent.mkdirs();
                }
                file.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
                Runtime.getInstance().logError(TAG, "create file error .");
                return this;
            }
        }
        mDownloadTask.setFile(target);
        return this;
    }

    private DownloadRequest target(@NonNull String target) {
        if (TextUtils.isEmpty(target)) {
            return this;
        }
        return target(new File(target));
    }


    private DownloadRequest targetDir(@NonNull String target) {
        if (TextUtils.isEmpty(target)) {
            return this;
        }
        File targetDir = new File(target);
        if (!targetDir.exists()) {
            targetDir.mkdirs();
        }
        mDownloadTask.setFile(targetDir);
        return this;
    }

    public DownloadRequest targetDir(@Nullable File target) {
        if (!target.exists()) {
            target.mkdirs();
        }
        mDownloadTask.setFile(target);
        return this;
    }

    private DownloadRequest targetDir(@NonNull File target, @NonNull String authority) {
        mDownloadTask.setFile(target, authority);
        return this;
    }


    public DownloadRequest setUniquePath(boolean uniquePath) {
        mDownloadTask.setUniquePath(uniquePath);
        return this;
    }

    public DownloadRequest setForceMonitor(boolean forceMonitor) {
        mDownloadTask.setForceMonitor(forceMonitor);
        return this;
    }

    public DownloadRequest setBlockMaxTime(long blockMaxTime) {
        mDownloadTask.blockMaxTime = blockMaxTime;
        return this;
    }

    public DownloadRequest setFileSuffix(String fileSuffix) {
        mDownloadTask.fileSuffix = fileSuffix;
        return this;
    }


    private DownloadRequest setContentLength(long contentLength) {
        mDownloadTask.mContentLength = contentLength;
        return this;
    }


    public DownloadRequest setDownloadTimeOut(long downloadTimeOut) {
        mDownloadTask.downloadTimeOut = downloadTimeOut;
        return this;
    }

    public DownloadRequest setConnectTimeOut(long connectTimeOut) {
        mDownloadTask.connectTimeOut = connectTimeOut;
        return this;
    }

    public DownloadRequest setOpenBreakPointDownload(boolean openBreakPointDownload) {
        mDownloadTask.mIsBreakPointDownload = openBreakPointDownload;
        return this;
    }

    public DownloadRequest setForceDownload(boolean force) {
        mDownloadTask.mIsForceDownload = force;
        return this;
    }

    private DownloadRequest setDownLoadDisplayFileName(String name) {
        mDownloadTask.downloadFileDisplayName = name;
        return this;
    }

    public DownloadRequest setEnableIndicator(boolean enableIndicator) {
        mDownloadTask.mEnableIndicator = enableIndicator;
        return this;
    }


    private DownloadRequest setQuickProgress(boolean quickProgress) {
        mDownloadTask.quickProgress = quickProgress;
        return this;
    }

    public DownloadRequest setTargetCompareMD5(String targetCompareMD5) {
        mDownloadTask.targetCompareMD5 = targetCompareMD5;
        return this;
    }

    public DownloadRequest setRetry(int retry) {
        mDownloadTask.setRetry(retry);
        return this;
    }

    private DownloadRequest setIcon(int icon) {
        mDownloadTask.mDownloadIcon = icon;
        return this;
    }

    public DownloadRequest setParallelDownload(boolean parallelDownload) {
        mDownloadTask.mIsParallelDownload = parallelDownload;
        return this;
    }

    public DownloadRequest addHeader(String key, String value) {
        if (mDownloadTask.mHeaders == null) {
            mDownloadTask.mHeaders = new HashMap<>();
        }
        mDownloadTask.mHeaders.put(key, value);
        return this;
    }


    public DownloadRequest autoOpenIgnoreMD5() {
        mDownloadTask.autoOpenIgnoreMD5();
        return this;
    }

    private DownloadRequest autoOpenWithMD5(String md5) {
        mDownloadTask.autoOpenWithMD5(md5);
        return this;
    }

    public DownloadRequest autoOpen(Boolean autoOpen) {
        mDownloadTask.autoOpen(autoOpen);
        return this;
    }

    private DownloadRequest closeAutoOpen() {
        mDownloadTask.closeAutoOpen();
        return this;
    }

    public File get() {
        return DownloadImpl.getInstance(mDownloadTask.mContext).call(mDownloadTask);
    }

    public DownloadRequest setDownloadListener(DownloadResultListener downloadResultListener) {
        mDownloadTask.setDownloadListener(downloadResultListener);
        return this;
    }

    public DownloadRequest setDownloadingListener(DownloadProgressListener downloadListener) {
        mDownloadTask.setDownloadingListener(downloadListener);
        return this;
    }

    public DownloadRequest
    setDownloadListenerAdapter(DownloadResultListenerAdapter downloadListenerAdapter) {
        mDownloadTask.setDownloadListenerAdapter(downloadListenerAdapter);
        return this;
    }

    public DownloadRequest setCalculateMD5(boolean calculateMD5) {
        mDownloadTask.setCalculateMD5(calculateMD5);
        return this;
    }

    public DownloadRequest quickProgress() {
        mDownloadTask.setQuickProgress(true);
        return this;
    }

    public DownloadTask getDownloadTask() {
        return this.mDownloadTask;
    }

    public void enqueue() {
        DownloadImpl.getInstance(mDownloadTask.mContext).enqueue(mDownloadTask);
    }

    private void enqueue(DownloadResultListener downloadResultListener) {
        mDownloadTask.setDownloadListener(downloadResultListener);
        DownloadImpl.getInstance(mDownloadTask.mContext).enqueue(mDownloadTask);
    }

    private void enqueue(DownloadProgressListener downloadProgressListener) {
        mDownloadTask.setDownloadingListener(downloadProgressListener);
        DownloadImpl.getInstance(mDownloadTask.mContext).enqueue(mDownloadTask);
    }

    private void enqueue(DownloadResultListenerAdapter downloadListenerAdapter) {
        setDownloadListenerAdapter(downloadListenerAdapter);
        DownloadImpl.getInstance(mDownloadTask.mContext).enqueue(mDownloadTask);
    }

    public void enqueue(DownLoadListener downLoadListener) {
        setDownloadListenerAdapter(new DownLoadAdapterListener(downLoadListener));
        DownloadImpl.getInstance(mDownloadTask.mContext).enqueue(mDownloadTask);
    }


    private class DownLoadAdapterListener extends DownloadResultListenerAdapter {
        private DownLoadListener listener;
        private long totalLength = 0;
        private long downloadLength = 0;
        private long upDownloadLength = 0;
        private int sendNum = 0;

        public DownLoadAdapterListener(DownLoadListener listener) {
            this.listener = listener;
        }

        @MainThread
        @Override
        public void onStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength, Extra extra) {
            if (listener != null) {
                mHandle.post(new Runnable() {
                    @Override
                    public void run() {
                        listener.onStart();
                    }
                });
            }
        }

        @Override
        public void onProgress(String url, long downloaded, long totalLength, long usedTime) {
            if (totalLength == 0) {
                return;
            }
            this.totalLength = totalLength;
            this.downloadLength = downloaded;
            downloadLength = (long) (Math.sqrt((downloadLength * 1.0f / totalLength) * 100) * totalLength / 10);

            float updateRate = 1 + downloaded * 1.0f / totalLength - 0.6f;

            float progressRate = (downloadLength - upDownloadLength) * 1.0f * 100 / totalLength;

            if (progressRate >= updateRate ||
                    downloaded >= totalLength) {
                HttpLog.d(" progressRate: " + progressRate + "-- updateRate: " + updateRate);
                upDownloadLength = downloadLength;
                runUiThread(runnable);
            }
        }

        @MainThread
        @Override
        public boolean onResult(Throwable throwable, File path, String url, Extra extra) {
            if (listener != null) {
                //下载完成数据上报
                if (throwable == null) {
                    runUiThread(new Runnable() {
                        @Override
                        public void run() {
                            listener.onComplete(url, path);
                        }
                    });

                } else {
                    //过滤取消下载回调
                    if (!throwable.getMessage().contains("canceled the download")) {
                        runUiThread(new Runnable() {
                            @Override
                            public void run() {
                                listener.onError(throwable);
                            }
                        });
                    }
                }
            }
            return false;
        }

        private Runnable runnable = new Runnable() {
            @Override
            public void run() {
                sendNum++;
                if (listener != null) {
                    HttpLog.d("****************Runnable update progress totalLength: " + totalLength + " downLoadLength: " + downloadLength + " templateLoaded: ");
                    listener.onProgress(totalLength, downloadLength);
                }
                HttpLog.d("sendNum: " + sendNum);
            }
        };

        @Override
        public void onDownloadStatusChanged(Extra extra, int status) {

        }

        private void runUiThread(Runnable runnable) {
            mHandle.post(runnable);
        }
    }

}
